#include <retro.h>
#include "retro.creator.h"

#define COLOR_POINT_COUNT     360
#define COLOR_WHEEL_RADIUS    100
#define COLOR_PICKER_RADIUS   10
#define COLOR_VALUE_MARGIN    5
#define COLOR_VALUE_INCREMENT 10

using namespace Retro::Creator;
using namespace Retro::Creator::GUI;

ColorSlider::ColorSlider()
{
  mMode   = ColorSliderValue;
  mState  = StateNormal;
  mValue  = 255;

  setColor(0xFFFFFFFF);
}

VOID  ColorSlider::getValueBounds(Gdip::Rect& oBounds)
{
  Geom::Size wSize = getSize();

  oBounds = Gdip::Rect(
    COLOR_PICKER_RADIUS / 2,
    COLOR_PICKER_RADIUS / 2,
    wSize.cx - COLOR_PICKER_RADIUS,
    wSize.cy - COLOR_PICKER_RADIUS);

  oBounds.Inflate(-1, -1);
}

VOID  ColorSlider::getValueCursorBounds(Gdip::Rect& oBounds)
{
  Geom::Size wSize = getSize();

  oBounds = Gdip::Rect(
    0,
    (255 - mValue) * (wSize.cy - 2 - COLOR_PICKER_RADIUS) / 255 + 1,
    wSize.cx,
    COLOR_PICKER_RADIUS);

  oBounds.Inflate(-1, 0);
}

VOID  ColorSlider::onControlPaint(ControlPaintEvent& iEvent)
{
  Gdip::Rect wValue;
  Gdip::Rect wCursorBounds;
  Gdip::GraphicsPath wPath;
  Gdip::Pen wBorderPen(0x7F000000);

  getValueBounds(wValue);
  getValueCursorBounds(wCursorBounds);

  Geom::HSVColor wColorTop    = mHSVColor;
  Geom::HSVColor wColorBottom = mHSVColor;
  Geom::HSVColor wColor       = mHSVColor;

  switch (mMode)
  {
  case ColorSliderAlpha:
    wColorTop.a = 255;
    wColorBottom.a = 0;
    wColor.a = mValue;

    Drawing::drawCheckerboard(wValue.Width / 2, wValue, iEvent.graphics);
    break;
  case ColorSliderValue:
    wColorTop.val = 255;
    wColorBottom.val = 0;
    wColor.val = mValue;
    break;
  }

  Geom::getRoundedRect(wPath, wValue, 3);

  iEvent.graphics.SetPixelOffsetMode(Gdip::PixelOffsetModeHalf);
  iEvent.graphics.FillPath(
    &Gdip::LinearGradientBrush(wValue,
    (COLOR)wColorTop,
    (COLOR)wColorBottom,
    Gdip::LinearGradientModeVertical),
    &wPath);
  iEvent.graphics.SetPixelOffsetMode(Gdip::PixelOffsetModeNone);

  wValue.Width--;
  wValue.Height--;

  wPath.Reset();
  Geom::getRoundedRect(wPath, wValue, 2);

  iEvent.graphics.DrawPath(&wBorderPen, &wPath);

  wPath.Reset();
  wValue.Inflate(-1, -1);
  Geom::getRoundedRect(wPath, wValue, 2);

  iEvent.graphics.DrawPath(&Gdip::Pen(0x7FFFFFFF), &wPath);

  if (!isFocused())
  {
    wCursorBounds.Inflate(
      -wCursorBounds.Width / 4,
      -wCursorBounds.Height / 4);
  }

  wPath.Reset();
  Geom::getRoundedRect(wPath, wCursorBounds, isFocused() ? 3 : 2);

  iEvent.graphics.SetPixelOffsetMode(Gdip::PixelOffsetModeHalf);
  iEvent.graphics.FillPath(&Gdip::SolidBrush((COLOR)wColor), &wPath);
  iEvent.graphics.SetPixelOffsetMode(Gdip::PixelOffsetModeNone);

  wCursorBounds.Width--;
  wCursorBounds.Height--;

  wPath.Reset();
  Geom::getRoundedRect(wPath, wCursorBounds, isFocused() ? 2 : 1);

  iEvent.graphics.DrawPath(&wBorderPen, &wPath);

  wPath.Reset();
  wCursorBounds.Inflate(-1, -1);
  Geom::getRoundedRect(wPath, wCursorBounds, isFocused() ? 2 : 1);

  iEvent.graphics.DrawPath(&Gdip::Pen(0x7FFFFFFF), &wPath);
}

VOID  ColorSlider::onMouseDown(Event::MouseEvent& iEvent)
{
  if (hitTestValue(iEvent.point))
  {
    mState = StatePressed;
    focus();
  }
}

VOID  ColorSlider::onMouseUp(Event::MouseEvent& iEvent)
{
  mState = StateNormal;
}

VOID  ColorSlider::onMouseMove(Event::MouseEvent& iEvent)
{
  hitTestValue(iEvent.point);
}

VOID  ColorSlider::onMouseWheel(Event::MouseWheelEvent& iEvent)
{
  setValue(mValue + iEvent.wheelDelta * COLOR_VALUE_INCREMENT / WHEEL_DELTA);
}

VOID  ColorSlider::onKeyDown(Event::KeyEvent& iEvent)
{
  switch (iEvent.vkCode)
  {
  case VK_UP:
    setValue(mValue + COLOR_VALUE_INCREMENT);
    break;
  case VK_DOWN:
    setValue(mValue - COLOR_VALUE_INCREMENT);
    break;
  }
}

VOID  ColorSlider::onFocus(Event::FocusEvent& iEvent)
{
  invalidate();
}

BOOL  ColorSlider::hitTestValue(const Geom::Point& iPoint)
{
  Gdip::Rect wBounds;
  getValueBounds(wBounds);

  if (!(mState == StatePressed ||
    (mState == StateNormal && GetKeyState(VK_LBUTTON) >> 1 &&
    wBounds.Contains(iPoint.x, iPoint.y))))
  {
    return FALSE;
  }

  LONG wPos = max(wBounds.Y, min(wBounds.GetBottom(), iPoint.y));

  setValue(255 - BYTE((wPos - wBounds.Y) * 255 / wBounds.Height));

  wBounds.Inflate(COLOR_PICKER_RADIUS, COLOR_PICKER_RADIUS);
  invalidate(wBounds);

  getValueBounds(wBounds);
  wBounds.Inflate(COLOR_PICKER_RADIUS, COLOR_PICKER_RADIUS);
  invalidate(wBounds);

  return TRUE;
}

VOID  ColorSlider::setValue(INT iValue)
{
  mValue = max(0, min(255, iValue));

  sendNotify(ColorSliderNotifySelection);
  invalidate();
}

VOID  ColorSlider::setColor(COLOR iValue)
{
  mRGBColor = Geom::RGBColor::get(iValue);
  mHSVColor = mRGBColor.toHSV();

  invalidate();
}
